use std::f64;
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast;

#[wasm_bindgen]
extern "C" {
    fn alert(s: &str);
}

#[wasm_bindgen]
pub fn greet(name: &str) {
    alert(&format!("Hello, {}!", name));
}

//完美，这个start 会让这个函数用胶水函数一初始化即调用，所以我们可以连初始化代码都rust化了
#[wasm_bindgen(start)]
pub fn run() -> Result<(), JsValue> {
    unsafe {
        return PROGRAM.init();
    }
}

struct MainProgram {
    counter: i32,
    span0: Option<web_sys::Element>,
}

static mut PROGRAM: MainProgram = MainProgram {
    counter: 0,
    span0: Option::None,
};

impl MainProgram {
    fn init(&mut self) -> Result<(), JsValue> {
        let window = web_sys::window().expect("no global `window` exists");
        let document = window.document().expect("should have a document on window");
        let body = document.body().expect("document should have a body");

        //add canvas
        let canvas = document
            .create_element("canvas")
            .unwrap()
            .dyn_into::<web_sys::HtmlCanvasElement>()
            .unwrap();
        body.append_child(&canvas).unwrap();
        let context = canvas
            .get_context("2d")
            .unwrap()
            .unwrap()
            .dyn_into::<web_sys::CanvasRenderingContext2d>()
            .unwrap();

        context.begin_path();

        // Draw the outer circle.
        context
            .arc(75.0, 75.0, 50.0, 0.0, f64::consts::PI * 2.0)
            .unwrap();

        // Draw the mouth.
        context.move_to(110.0, 75.0);
        context.arc(75.0, 75.0, 35.0, 0.0, f64::consts::PI).unwrap();

        // Draw the left eye.
        context.move_to(65.0, 65.0);
        context
            .arc(60.0, 65.0, 5.0, 0.0, f64::consts::PI * 2.0)
            .unwrap();

        // Draw the right eye.
        context.move_to(95.0, 65.0);
        context
            .arc(90.0, 65.0, 5.0, 0.0, f64::consts::PI * 2.0)
            .unwrap();

        context.stroke();

        // Manufacture the element we're gonna append
        MainProgram::add_span(&body, "调用canvas 画个笑脸");
        MainProgram::add_span(&body, "这个代码是Rust编译为WASM来执行的");
        MainProgram::add_span(&body, "启动ts代码如下：");
        MainProgram::add_span(&body,"declare function  wasm_bindgen (module_or_path: RequestInfo | BufferSource | WebAssembly.Module): Promise<any>;");
        MainProgram::add_span(
            &body,
            "window.onload = async () =>\n
        {\n
            var wasm = await wasm_bindgen(\"./wasm_bg.wasm\");\n
        }\n
        ",
        );
        self.span0 = Some(MainProgram::add_span(&body, "span for Timer"));
        MainProgram::request_animation_frame(MainProgram::update_static);
        return Result::Ok(());
    }
    fn settext(&mut self, _txt: &str) {
        let span = &self.span0;
        match span {
            Some(v) => v.set_inner_html(_txt),
            None => {}
        };
    }
    fn update(&mut self) {
        self.counter += 1;
        let txt = format! {"counter={}",self.counter};
        self.settext(&txt);
    }
    fn update_static() {
        unsafe {
            PROGRAM.update();
        }
        MainProgram::request_animation_frame(MainProgram::update_static);
    }
    fn request_animation_frame(_update: fn()) {
        let funcbox = Box::new(_update) as Box<dyn Fn()>;
        let func = Closure::wrap(funcbox);
        let funcref = func.as_ref().unchecked_ref();
        let window = web_sys::window().expect("no global `window` exists");
        window.request_animation_frame(funcref).unwrap();
        func.forget();
    }

    fn add_span(parent: &web_sys::HtmlElement, txt: &str) -> web_sys::Element {
        let window = web_sys::window().expect("no global `window` exists");
        let document = window.document().expect("should have a document on window");
        let val = document.create_element("p").unwrap();
        val.set_inner_html(txt);
        parent.append_child(&val).unwrap();
        return val;
    }
}
